<script>
import { Container, Draggable } from 'vue3-smooth-dnd'
import db from '@/db'
import folderService from '@/service/folderService'
import { useObservable } from '@vueuse/rxjs'
import { liveQuery } from 'dexie'
import { mapActions, mapState } from 'vuex'
import conversationService from '@/service/conversationService'
import { ref, watch } from 'vue'

export default {
  name: 'FolderList',
  components: { Container, Draggable },
  data() {
    return {
      openedFolder: [],
      folderDropPlaceholder: {
        className: 'folder-drop-preview',
      },
      conversationDropPlaceholder: {
        className: 'conversation-drop-preview',
      },
      deleteFolderChecker: {
        id: null,
      },
      deleteConversationChecker: {
        id: null,
      },
      editFolder: {
        newFolderActive: false,
        title: null,
        id: null,
        updating: false,
      },
      editConversation: {
        title: null,
        id: null,
        updating: false,
      },
      showMoveMenu: [],
    }
  },
  computed: {
    ...mapState({
      isCreateFolderEnable: state => state.folder.isCreateFolderEnable,
    }),
    folders() {
      // 如果没有从数据库中获取到数据，就是使用空数组
      // 将数据复制一份，然后添加一个unsorted的文件夹
      const folders = this.foldersFormDB.value ? [...this.foldersFormDB.value] : []

      // 设置icon, 如果是unsorted, 设置专属icon
      // 否则用默认的icon
      folders.forEach(folder => {
        // eslint-disable-next-line no-param-reassign
        folder.icon = (folder.type === 'unsorted') ? 'mdi-folder-arrow-left-right' : 'mdi-folder'
      })

      return folders
    },
    conversations() {
      // 如果没有从数据库中获取到数据，就是使用空数组
      const conversations = this.conversationsFromDB.value ? [...this.conversationsFromDB.value] : []

      // 遍历arr，进行排序，isPin的排在前面
      const sorted = conversationService.sorted(conversations)
      const conversationGroup = []
      this.folders.forEach(folder => {
        conversationGroup[folder.id] = []
      })

      // 排序后按照folderId进行分组, 存在this.conversations[folderId]中
      // 如果folderId为null，那么就存在this.conversations[1]中
      sorted.forEach(item => {
        // 如果folderId为null或者this.folders这个id没有，那么就存在this.conversations[1]中
        const isDefaultFolder = item.folderId === null || !this.folders.find(folder => folder.id === item.folderId)
        const folderId = isDefaultFolder ? 1 : item.folderId

        conversationGroup[folderId].push(item)
      })

      return conversationGroup
    },
  },
  methods: {
    ...mapActions({
      setIsCreateFolderDisabled: 'folder/setIsCreateFolderDisabled',
    }),

    /**
     * 移动文件夹
     * @param dropResult
     */
    moveFolder(dropResult) {
      const { removedIndex, addedIndex } = dropResult
      if (removedIndex === null && addedIndex === null) return

      // 从folders取出被拖拽的元素(removedIndex)
      const { id } = this.folders[removedIndex]

      folderService.moveFolder(id, removedIndex, addedIndex)

      // this.items = this.applyDrag(this.items, dropResult)
    },

    /**
     * 删除文件夹
     */
    deleteFolder(id) {
      this.deleteFolderChecker.id = null
      folderService.removeFolder(id)
    },
    clearCreateFolderField() {
      this.editFolder = {
        title: null,
        id: null,
        updating: false,
      }
      this.setIsCreateFolderDisabled()
    },

    /**
     * 添加文件夹
     */
    addFolder() {
      if (this.editFolder.title === ''
        || this.editFolder.title === null
        || this.editFolder.updating
      ) {
        this.clearCreateFolderField()

        return
      }
      this.editFolder.updating = true

      folderService.addFolder(this.editFolder.title).then(() => {
        this.clearCreateFolderField()
      })
    },

    /**
     * 更新文件夹标题
     */
    updateFolderTitle() {
      if (this.editFolder.title === ''
        || this.editFolder.title === null
        || this.editFolder.updating
      ) {
        this.editFolder = {
          title: null,
          id: null,
          updating: false,
        }

        return
      }
      this.editFolder.updating = true

      db.folder.update(this.editFolder.id, {
        title: this.editFolder.title,
      }).then(() => {
        this.editFolder = {
          title: null,
          id: null,
          updating: false,
        }
      })
    },

    /**
     * 设置会话的是否置顶
     * @param id
     * @param isPin
     */
    setPinConversation(id, isPin) {
      db.conversation.update(id, {
        isPin,
      })
    },

    /**
     * 移动会话
     */
    conversationMove(folderId, dropResult) {
      const { payload, addedIndex } = dropResult

      if (addedIndex === null) return

      // 移动会话
      conversationService.move(payload.id, folderId, addedIndex)
    },
    conversationMoveByMenu(folderId, conversationId) {
      // 移动会话
      conversationService.move(conversationId, folderId)
      this.showMoveMenu[conversationId] = false
    },

    /**
     * 删除会话
     */
    removeConversation(id) {
      conversationService.remove(id).catch(err => {
        this.$store.dispatch('alert', err.message)
      }).finally(() => {
        this.deleteConversationChecker.id = null
      })
    },

    /**
     * 修改会话标题
     */
    updateConversationTitle() {
      if (this.editConversation.title === ''
        || this.editConversation.title === null
        || this.editConversation.updating
      ) {
        this.editConversation = {
          title: null,
          id: null,
          updating: false,
        }

        return
      }
      this.editConversation.updating = true

      conversationService.updateTitle(this.editConversation.id, this.editConversation.title).catch(err => {
        this.$store.dispatch('alert', err.message)
      }).finally(() => {
        this.editConversation = {
          title: null,
          id: null,
          updating: false,
        }
      })
    },

    /**
     * 打开会话
     */
    toConversation(id) {
      // 当前chrome tab跳转到会话
      chrome.tabs.update({
        url: `https://chat.openai.com/c/${id}`,
      })
    },
  },
  watch: {
    isCreateFolderEnable(val) {
      if (val) {
        this.$nextTick(() => {
          this.$refs.createFolderWrapper.scrollIntoView({
            behavior: 'auto',
            block: 'end',
          })
        })
      }
    },
    openedFolder(val) {
      const openedFolder = [...val]

      // 记录当前打开的文件夹，存入vuex中，用于下次打开时，自动打开
      chrome.storage.local.set({ openedFolder })
    },
  },
  setup() {
    // 校验是否存在默认文件夹
    db.folder.get({ id: 1 }).then(res => {
      if (res === undefined) {
        folderService.createDefaultFolder()
      }
    })

    const user = useObservable(liveQuery(() => db.user.get({ isCurrent: 1 })))
    const foldersFormDB = ref([])
    const conversationsFromDB = ref([])

    watch(user, newValue => {
      foldersFormDB.value = useObservable(
        liveQuery(() => db.folder.where({ userId: newValue.id })
          .or('userId').equals(0)
          .sortBy('order')),
      )
      conversationsFromDB.value = useObservable(
        liveQuery(() => db.conversation.where({
          userId: newValue.id,
        }).toArray()),
      )
    })

    return {
      db,
      user,
      foldersFormDB,
      conversationsFromDB,
    }
  },
  mounted() {
    this.$nextTick(() => {
      // 获取当前打开的文件夹
      chrome.storage.local.get(['openedFolder'], res => {
        if (res.openedFolder) {
          this.openedFolder = res.openedFolder
        }
      })
    })
  },
}
</script>

<template>
  <v-list
    :lines="false"
    density="compact"
    :nav="true"
    v-model:opened="openedFolder"
    id="folderWrapper"
  >
    <Container
      orientation="vertical"
      :drop-placeholder="folderDropPlaceholder"
      non-drag-area-selector=".drag-disabled"
      @drop="moveFolder"
    >
      <Draggable
        v-for="folder in folders"
        :key="folder.id"
      >

        <v-list-group
          :value="folder.id"
          v-if="editFolder.id !== folder.id && deleteFolderChecker.id !== folder.id"
        >
          <template v-slot:activator="{ isOpen, props: actProps }">
            <v-hover
              v-slot="{ isHovering, props: hoverProps }"
            >
              <v-list-item
                v-bind="{ ...actProps, ...hoverProps }"
                :prepend-icon="isOpen ? 'mdi-folder-open' : folder.icon"
                color="teal"
              >
                <v-list-item-title>
                  {{ folder.title }}
                </v-list-item-title>
                <template v-slot:append>
                  <v-btn
                    v-show="isHovering"
                    title="rename"
                    icon="mdi-rename-outline"
                    size="x-small"
                    color="grey-darken-1"
                    variant="plain"
                    @click.stop="this.editFolder.id = folder.id; this.editFolder.title = folder.title"
                  />
                  <v-btn
                    v-show="isHovering && folder.id !== 1"
                    title="delete"
                    icon="mdi-delete-outline"
                    size="x-small"
                    color="grey-darken-1"
                    variant="plain"
                    @click.stop="deleteFolderChecker.id = folder.id"
                  />
                  <v-chip
                    class="ma-2"
                    size="x-small"
                  >
                    {{ conversations[folder.id]?.length || 0 }}
                  </v-chip>
                </template>
              </v-list-item>
            </v-hover>
          </template>
          <Container
            @drop="(e) => conversationMove(folder.id, e)"
            :get-child-payload="(index) => conversations[folder.id][index]"
            group-name="conversation"
            drag-class="conversation-drop-ghost"
            drop-class="conversation-drop-ghost-drop"
            non-drag-area-selector=".drag-disabled"
            :drop-placeholder="conversationDropPlaceholder"
          >
            <Draggable v-for="conversation in conversations[folder.id]" :key="conversation.id">
              <v-hover
                v-if="editConversation.id !== conversation.id && deleteConversationChecker.id !== conversation.id">
                <template
                  v-slot="{ isHovering, props: hoverProps }"
                >
                  <v-list-item
                    :value="conversation.id"
                    :active="false"
                    v-bind="hoverProps"
                    prepend-icon="mdi-message-outline"
                    @click.stop="toConversation(conversation.id)"
                  >
                    <v-list-item-title>
                      {{ conversation.title }}
                    </v-list-item-title>
                    <template v-slot:append>
                      <v-btn
                        v-show="isHovering || showMoveMenu[conversation.id]"
                        title="rename"
                        icon="mdi-rename-outline"
                        size="x-small"
                        color="grey-darken-1"
                        variant="plain"
                        @click.stop="this.editConversation.id = conversation.id; this.editConversation.title = conversation.title"
                      />
                      <v-btn
                        v-show="isHovering || showMoveMenu[conversation.id]"
                        title="delete"
                        icon="mdi-delete-outline"
                        size="x-small"
                        color="grey-darken-1"
                        variant="plain"
                        @click.stop="deleteConversationChecker.id = conversation.id"
                      />
                      <v-btn
                        title="move"
                        v-show="isHovering || showMoveMenu[conversation.id]"
                        icon="mdi-folder-move-outline"
                        size="x-small"
                        color="grey-darken-1"
                        variant="plain"
                        :id="`move-menu-activator-${ conversation.id }`"
                      />
                      <v-menu
                        :activator="`#move-menu-activator-${ conversation.id }`"
                        location="left"
                        v-model="showMoveMenu[conversation.id]"
                      >
                        <v-list
                          :lines="false"
                          density="compact"
                          :nav="true"
                        >
                          <v-list-item>
                            <v-list-item-title>Move to</v-list-item-title>
                          </v-list-item>
                          <v-divider></v-divider>
                          <v-list-item
                            v-for="(folder, index) in folders"
                            :key="index"
                            :prepend-icon="folder.icon"
                            @click="conversationMoveByMenu(folder.id, conversation.id)"
                          >
                            <v-list-item-title>
                              {{ folder.title }}
                            </v-list-item-title>
                          </v-list-item>
                        </v-list>
                      </v-menu>

                      <v-btn
                        v-show="conversation.isPin || isHovering || showMoveMenu[conversation.id]"
                        :title="conversation.isPin ? 'unpin' : 'pin'"
                        :icon="conversation.isPin ? 'mdi-pin' : 'mdi-pin-outline'"
                        size="x-small"
                        :color="conversation.isPin ? 'teal-lighten-1' : 'grey-darken-1'"
                        :variant="conversation.isPin ? 'text' : 'plain'"
                        @click.stop="setPinConversation(conversation.id, !conversation.isPin)"
                      />
                    </template>

                  </v-list-item>
                </template>
              </v-hover>
              <v-list-item
                :active="false"
                prepend-icon="mdi-message-outline"
                class="drag-disabled"
                v-else-if="editConversation.id === conversation.id"
              >
                <v-text-field
                  v-model="editConversation.title"
                  :autofocus="true"
                  base-color="teal-lighten-2"
                  color="teal-lighten-1"
                  variant="outlined"
                  density="compact"
                  @blur.stop="updateConversationTitle"
                  @keyup.enter.stop="updateConversationTitle"
                  single-line
                  hide-details
                ></v-text-field>
              </v-list-item>
              <v-list-item
                :active="false"
                prepend-icon="mdi-delete"
                class="drag-disabled"
                v-else-if="deleteConversationChecker.id === conversation.id"
              >
                <v-list-item-title>
                  Delete "{{ conversation.title }}"?
                </v-list-item-title>
                <template v-slot:append>
                  <v-btn
                    title="yes"
                    icon="mdi-check"
                    size="x-small"
                    color="grey-darken-1"
                    variant="text"
                    @click.stop="removeConversation(conversation.id)"
                  />
                  <v-btn
                    title="no"
                    icon="mdi-close"
                    size="x-small"
                    color="grey-darken-1"
                    variant="text"
                    @click.stop="deleteConversationChecker.id = null"
                  />
                </template>
              </v-list-item>
            </Draggable>
          </Container>
          <p
            v-if="!conversations[folder.id]?.length"
            class="text-caption text-grey-darken-1 text-center"
          >Drag conversation to here...</p>
        </v-list-group>
        <v-list-item
          :prepend-icon="folder.icon"
          color="teal"
          class="drag-disabled"
          v-else-if="editFolder.id === folder.id"
        >
          <v-text-field
            v-model="editFolder.title"
            :autofocus="true"
            base-color="teal-lighten-2"
            color="teal-lighten-1"
            variant="outlined"
            density="compact"
            @blur.stop="updateFolderTitle"
            @keyup.enter.stop="updateFolderTitle"
            single-line
            hide-details
          ></v-text-field>
        </v-list-item>
        <v-list-item
          :active="false"
          prepend-icon="mdi-delete"
          class="drag-disabled"
          v-else-if="deleteFolderChecker.id === folder.id"
        >
          <v-list-item-title>
            Delete folder "{{ folder.title }}"?
          </v-list-item-title>
          <template v-slot:append>
            <v-btn
              title="yes"
              icon="mdi-check"
              size="x-small"
              color="grey-darken-1"
              variant="text"
              @click.stop="deleteFolder(folder.id)"
            />
            <v-btn
              title="no"
              icon="mdi-close"
              size="x-small"
              color="grey-darken-1"
              variant="text"
              @click.stop="deleteFolderChecker.id = null"
            />
          </template>
        </v-list-item>

      </Draggable>
    </Container>
    <div
      ref="createFolderWrapper"
      id="createFolderWrapper"
    >
      <v-list-item
        prepend-icon="mdi-folder"
        color="teal"
        v-if="isCreateFolderEnable"
      >
        <v-text-field
          :autofocus="true"
          v-model="this.editFolder.title"
          base-color="teal-lighten-2"
          color="teal-lighten-1"
          variant="outlined"
          density="compact"
          @blur.stop="addFolder"
          @keyup.enter.stop="addFolder"
          @keyup.esc.stop="clearCreateFolderField"
          single-line
          hide-details
        ></v-text-field>
      </v-list-item>
    </div>
  </v-list>

</template>

<style lang="scss" scoped>
#folderWrapper {
  height: 600px - 64 - 16 * 2 - 34;
  overflow-y: auto;
}

::v-deep.v-text-field {
  $max-height: 28px !important;
  max-height: $max-height;

  input {
    max-height: $max-height;
    min-height: $max-height;
    padding-left: 8px;
  }
}

</style>
